package com.autumn.mybatis.mapper;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.GenerationType;

import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
import org.apache.ibatis.session.Configuration;

import com.autumn.mybatis.metadata.EntityColumn;
import com.autumn.mybatis.metadata.EntityTable;
import com.autumn.mybatis.provider.InsertSelectKey;

/**
 * 实体驱动提供者
 *
 * @author 杨昌国
 *         <p>
 *         2017-10-11 17:49:40
 */
public class EntityMapperProvider extends MapperProvider {

	private static final String METAOBJECT_PROPERTY_KEY_GENERATOR = "keyGenerator";
	private static final String METAOBJECT_PROPERTY_KEY_PROPERTIES = "keyProperties";
	private static final String METAOBJECT_PROPERTY_KEY_COLUMNS = "keyColumns";

	/**
	 * @param mapperClass
	 * @param rapperRegister
	 */
	public EntityMapperProvider(Class<?> mapperClass, MapperRegister rapperRegister) {
		super(mapperClass, rapperRegister);
	}

	/**
	 * @param ms
	 * @param column
	 */
	private void setInsertSelectKey(MappedStatement ms, EntityColumn column) {
		String keyId = ms.getId() + SelectKeyGenerator.SELECT_KEY_SUFFIX;
		if (ms.getConfiguration().hasKeyGenerator(keyId)) {
			return;
		}
		Class<?> entityClass = column.getTable().getEntityClass();
		Configuration configuration = ms.getConfiguration();
		InsertSelectKey insertSelectKey = this.getDbProvider().getInsertBuilder().getIdentitySelectKey(column);
		KeyGenerator keyGenerator;
		SqlSource sqlSource = new RawSqlSource(configuration, insertSelectKey.getSelectKeySql(), entityClass);
		MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, keyId, sqlSource,
				SqlCommandType.SELECT);
		statementBuilder.resource(ms.getResource());
		statementBuilder.fetchSize(null);
		statementBuilder.statementType(StatementType.STATEMENT);
		statementBuilder.keyGenerator(new NoKeyGenerator());
		statementBuilder.keyProperty(column.getPropertyName());
		statementBuilder.keyColumn(null);
		statementBuilder.databaseId(null);
		statementBuilder.lang(configuration.getDefaultScriptingLanguageInstance());
		statementBuilder.resultOrdered(false);
		statementBuilder.resultSets(null);
		statementBuilder.timeout(configuration.getDefaultStatementTimeout());

		List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
		ParameterMap.Builder inlineParameterMapBuilder = new ParameterMap.Builder(configuration,
				statementBuilder.id() + "-Inline", entityClass, parameterMappings);
		statementBuilder.parameterMap(inlineParameterMapBuilder.build());

		List<ResultMap> resultMaps = new ArrayList<ResultMap>();
		ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder(configuration,
				statementBuilder.id() + "-Inline", column.getJavaType(), new ArrayList<ResultMapping>(), null);
		resultMaps.add(inlineResultMapBuilder.build());
		statementBuilder.resultMaps(resultMaps);
		statementBuilder.resultSetType(null);
		statementBuilder.flushCacheRequired(false);
		statementBuilder.useCache(false);
		statementBuilder.cache(null);
		MappedStatement statement = statementBuilder.build();
		configuration.addMappedStatement(statement);
		MappedStatement keyStatement = configuration.getMappedStatement(keyId, false);
		keyGenerator = new SelectKeyGenerator(keyStatement, insertSelectKey.isExecuteBefore());
		configuration.addKeyGenerator(keyId, keyGenerator);
		MetaObject msObject = SystemMetaObject.forObject(ms);
		msObject.setValue(METAOBJECT_PROPERTY_KEY_GENERATOR, keyGenerator);
		msObject.setValue(METAOBJECT_PROPERTY_KEY_PROPERTIES, column.getTable().getKeyPropertieNames());
		msObject.setValue(METAOBJECT_PROPERTY_KEY_COLUMNS, column.getTable().getKeyColumnNames());
	}

	/**
	 * 插入
	 *
	 * @param ms
	 * @return
	 */
	public String insert(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		for (EntityColumn col : table.getColumns()) {
			if (col.isPrimaryKey() && col.getGenerationType().equals(GenerationType.IDENTITY)) {
				setInsertSelectKey(ms, col);
			}
		}
		return this.getDbProvider().getInsertBuilder().getInsertSql(table);
	}

	/**
	 * 批量插入
	 *
	 * @param
	 * @return
	 */
	public String batchInsert(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getInsertBuilder().getBatchInsertSql(table, IEntityMapper.ARG_NAME_LIST);
	}

	/**
	 * 快速清除表数据
	 *
	 * @param ms
	 * @return
	 */
	public String truncateTable(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getDeleteBuilder().getTruncateByTable(table);
	}

	/**
	 * 删除所有数据
	 *
	 * @param ms
	 * @return
	 */
	public String deleteByAll(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getDeleteBuilder().getDeleteByAll(table);
	}

	/**
	 * 基于主键进行删除
	 *
	 * @param ms
	 * @return
	 */
	public String deleteByPrimaryKey(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getDeleteBuilder().getDeleteByPrimaryKeySql(table);
	}

	/**
	 * 基于条件进行删除
	 *
	 * @param ms
	 * @return
	 */
	public String deleteByWhere(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getDeleteBuilder().getDeleteByWhereSql(table, IEntityMapper.ARG_NAME_SECTION);
	}

	/**
	 * 基于主键进行更新,跳过null值
	 *
	 * @param ms
	 * @return
	 */
	public String updateNotNull(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getUpdateBuilder().getUpdateSql(table, true);
	}

	/**
	 * 基于主键进行更新,全部更新
	 *
	 * @param ms
	 * @return
	 */
	public String update(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getUpdateBuilder().getUpdateSql(table, false);
	}

	/**
	 * 修改指定字段
	 *
	 * @param ms
	 * @return
	 */
	public String updateBySpecify(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getUpdateBuilder().getUpdateMapSql(table, IEntityMapper.ARG_NAME_SECTION);
	}

	/**
	 * 基于主键查询
	 *
	 * @param ms
	 * @return
	 */
	public String get(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		setResultType(ms, table);
		return this.getDbProvider().getQueryBuilder().getSelectByPrimaryKeySql(table);
	}

	/**
	 * 基于主键查询
	 *
	 * @param ms
	 * @return
	 */
	public String getByLock(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		setResultType(ms, table);
		return this.getDbProvider().getQueryBuilder().getSelectByPrimaryKeyByLockSql(table, IEntityMapper.ARG_NAME_KEY,
				IEntityMapper.ARG_NAME_LOCK_MODE);
	}

	/**
	 * 全部查询
	 *
	 * @param ms
	 * @return
	 */
	public String selectAll(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		setResultType(ms, table);
		return this.getDbProvider().getQueryBuilder().getSelectAllSql(table);
	}

	/**
	 * 全部查询
	 *
	 * @param ms
	 * @return
	 */
	public String selectAllByLock(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		setResultType(ms, table);
		return this.getDbProvider().getQueryBuilder().getSelectAllLockSql(table, IEntityMapper.ARG_NAME_LOCK_MODE);
	}

	/**
	 * 查询得到list
	 *
	 * @param ms
	 * @return
	 */
	public String selectList(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		setResultType(ms, table);
		return this.getDbProvider().getQueryBuilder().getSelectListSql(table, IEntityMapper.ARG_NAME_SECTION);
	}

	/**
	 * 查询实体首条记录
	 *
	 * @param ms
	 * @return
	 */
	public String selectFirst(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		setResultType(ms, table);
		return this.getDbProvider().getQueryBuilder().getSelectFirstSql(table, IEntityMapper.ARG_NAME_SECTION);
	}

	/**
	 * 指定查询结果
	 *
	 * @param ms
	 * @return
	 */
	public String selectBySpecify(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getQueryBuilder().getSelectBySpecifySql(table, IEntityMapper.ARG_NAME_SECTION);
	}

	/**
	 * 获取首条记录
	 *
	 * @param ms
	 * @return
	 */
	public String selectBySpecifyFirst(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getQueryBuilder().getSelectBySpecifyFirstSql(table, IEntityMapper.ARG_NAME_SECTION);
	}

	/**
	 * 全部查询记录数
	 *
	 * @param ms
	 * @return
	 */
	public String count(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getQueryBuilder().getCountByAllSql(table);
	}

	/**
	 * 全部查询记录数(锁)
	 *
	 * @param ms
	 * @return
	 */
	public String countByLock(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getQueryBuilder().getCountByAllLockSql(table, IEntityMapper.ARG_NAME_LOCK_MODE);
	}

	/**
	 * 根据条件查询记录数
	 *
	 * @param ms
	 * @return
	 */
	public String countByWhere(MappedStatement ms) {
		EntityTable table = this.getEntityTable(this.getEntityClass(ms));
		return this.getDbProvider().getQueryBuilder().getCountByWhereSql(table, IEntityMapper.ARG_NAME_SECTION);
	}

	/**
	 * 调用存储过程
	 *
	 * @param ms
	 * @return
	 */
	public String executeCommand(MappedStatement ms) {
		return this.getDbProvider().getCommandBuilder().getExecuteCommandSql(ICustomBaseMapper.ARG_NAME_COMMANDTEXT);
	}

	/**
	 * 调用存储过程
	 *
	 * @param ms
	 * @return
	 */
	public String executeProcedure(MappedStatement ms) {
		return this.getDbProvider().getCommandBuilder().getExecuteProcedureSql(ICustomBaseMapper.ARG_NAME_FUNCTION);
	}
}
